#include "hopfield.h"

int main(int argc, char* argv[])
{
	// read from images from dir
	//char dirName[256] = argv[1];
	char dirName[256] = "training_data";

	DIR* dir = opendir(dirName);
	if (dir != NULL)
	{
		dirent* ent;

		while ((ent = readdir(dir)) != NULL)
		{
			char* aux = strrchr(ent->d_name, '.');
			if (strcmp(aux, ".bmp") == 0)
			{
				char imagePath[256];
				sprintf(imagePath, "%s\\%s", dirName, ent->d_name);
				IplImage* img = cvLoadImage(imagePath, CHANNELS > 1 ? CV_LOAD_IMAGE_COLOR : CV_LOAD_IMAGE_GRAYSCALE);
				images.push_back(img);//TODO: free memory
			}
		}
	}

	// Load the Hopfield state => for each image, load a vector that represents the image. Each image is a collumn at P matrix
	P = (unsigned char**)malloc(WIDTH * HEIGHT * CHANNELS * sizeof(unsigned char*));
	for (int i = 0; i < WIDTH * HEIGHT * CHANNELS; i++)
	{
		P[i] = (unsigned char*)malloc(images.size() * sizeof(unsigned char));
	}

	for (int j = 0; j < images.size(); j++)
	{
		unsigned char* Xj = loadPattern(images.at(j));
		
		for(int i = 0; i < WIDTH * HEIGHT * CHANNELS; i++)
			P[i][j] = Xj[i];

		free(Xj);
	}

	// just show the images on the screen, two per second
	for (int i = 0; i < images.size(); i++)
	{
		unsigned char X[WIDTH * HEIGHT * CHANNELS];
		for(int j = 0; j < WIDTH * HEIGHT * CHANNELS; j++)
			X[j] = P[j][i];
		IplImage* imgFromPattern = loadImage(X);
		cv::imshow("image", imgFromPattern);
		cvWaitKey(500);
	}

	Weights = createWeights();

	//release memory
	destroyWeights();
	destroyState();
	destroyImages();

	printf("Hopfield Finished\n");
}

int getBit(char byte, int pos)
{
	if ( byte&(1<<pos) )
		return 1;

	return -1;
}

unsigned char* loadPattern(IplImage* img)
{
	// sizeof(unsigned char) = 1 byte (8 bits)
	unsigned char* Xk = (unsigned char*)malloc(CHANNELS * WIDTH * HEIGHT * sizeof(unsigned char));

	for (int y = 0; y < img->height; y++)
	{
		for (int x = 0; x < img->width; x++)
		{
			Xk[y * img->widthStep + CHANNELS * x + 0] = img->imageData[y * img->widthStep + CHANNELS * x + 0]; //blue
			if (CHANNELS > 1)
			{
				Xk[y * img->widthStep + CHANNELS * x + 1] = img->imageData[y * img->widthStep + CHANNELS * x + 1]; //green
				Xk[y * img->widthStep + CHANNELS * x + 2] = img->imageData[y * img->widthStep + CHANNELS * x + 2]; //red
			}
		}
	}

	return Xk;
}

IplImage* loadImage(unsigned char* Xk)
{
	IplImage* img = cvCreateImage(cvSize(WIDTH, HEIGHT), IPL_DEPTH_8U, CHANNELS);
	int widthStep = CHANNELS * WIDTH;

	for (int y = 0; y < HEIGHT; y++)
	{
		for (int x = 0; x < WIDTH; x++)
		{
			img->imageData[y * widthStep + CHANNELS * x + 0] = Xk[y * widthStep + CHANNELS * x + 0]; //blue
			if (CHANNELS > 1)
			{
				img->imageData[y * widthStep + CHANNELS * x + 1] = Xk[y * widthStep + CHANNELS * x + 1]; //green
				img->imageData[y * widthStep + CHANNELS * x + 2] = Xk[y * widthStep + CHANNELS * x + 2]; //red
			}
		}
	}

	return img;
}

double** createWeights()
{
	int n = CHANNELS * WIDTH * HEIGHT * 8;
	double** W = (double**)calloc(n, sizeof(double*));
	for (int i = 0; i < n; i++)
		W[i] = (double*)calloc(n, sizeof(double));

	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < n; j++)
		{
			W[i][j] = dot(P[i / 8], P[j / 8], i % 8, j % 8) / n;
		}
	}

	for (int i = 0; i < CHANNELS * WIDTH * HEIGHT; i++)
		W[i][i] = 0.0;

	return W;
}

double dot(unsigned char* v1, unsigned char* v2, int pos_1, int pos_2)
{
	double result = 0.0;
	for (int i = 0; i < images.size(); i++) //iterate over patterns
		result += getBit(v1[i], pos_1) * getBit(v2[i], pos_2);

	return result;
}

void destroyWeights()
{
	int n = CHANNELS * WIDTH * HEIGHT * 8;
	for (int i = 0; i < n; i++)
		free(Weights[i]);

	free(Weights);
}

void destroyState()
{
	for (int i = 0; i < images.size(); i++)
		free(P[i]);

	free(P);
}

void destroyImages()
{
	for(int i = 0; i < images.size(); i++)
		cvReleaseImage(&images.at(i));
}